// C++ Templates: 第四章 变参模板简介
// 4.1 变参模板基础
// 变参模板(Variadic Templates)是C++11引入的一项重要特性，它允许模板接受任意数量的模板参数。这一特性极大地增强了C++模板的灵活性和表达能力。

// 基本语法
// 变参模板使用省略号(...)语法来表示可变数量的参数：
// 变参函数模板
template <typename... Types>
void func(Types... args);

// 变参类模板
template <typename... Types>
class Tuple;
// 这里的Types是一个模板参数包，它可以包含零个或多个类型。args是一个函数参数包，它包含与Types对应的实际参数。

// 使用示例
// 变参函数模板示例
template <typename... Types>
void printAll(Types... args)
{
    // 实现将在后面讨论
}

// 调用示例
printAll(42, "hello", 3.14, 'a'); // 接受4个不同类型的参数
printAll();                       // 不传递任何参数也是有效的

// 4.2 包展开
// 要处理参数包，我们需要使用包展开(Pack Expansion)，它也使用省略号语法
template <typename... Types>
void printAll(Types... args)
{
    // 错误：不能直接操作参数包
    // std::cout << args;

    // 需要展开参数包
}

// 递归方式展开
// 早期处理参数包的常见方法是递归：
// 基本情况：处理空参数包
void printAll()
{
    std::cout << std::endl;
}

// 递归情况：处理第一个参数，然后递归处理剩余参数
template <typename T, typename... Types>
void printAll(T firstArg, Types... args)
{
    std::cout << firstArg << " ";
    printAll(args...); // 递归调用，处理剩余参数
}
// 这种方法通过模板特化和递归调用来处理参数包，每次处理一个参数。

// 折叠表达式（C++17）
// C++17引入了折叠表达式，大大简化了参数包的处理：
template <typename... Types>
void printAll(Types... args)
{
    // 使用折叠表达式
    ((std::cout << args << " "), ...);
    std::cout << std::endl;
}
// 这里使用了逗号运算符和一元折叠表达式，对参数包中的每个元素应用相同的操作。

// 4.3 参数包的大小和索引
// 我们可以使用sizeof...运算符获取参数包中的元素数量：
template <typename... Types>
void printSize(Types... args)
{
    std::cout << "Number of arguments: " << sizeof...(args) << std::endl;
    std::cout << "Number of types: " << sizeof...(Types) << std::endl;
}

printSize(1, 'a', "hello"); // 输出：Number of arguments: 3
                            //       Number of types: 3

// 4.4 变参模板的应用
// 完美转发
// 变参模板结合完美转发可以创建通用的包装函数：
template <typename Func, typename... Args>
auto invoke(Func &&func, Args &&...args) -> decltype(std::forward<Func>(func)(std::forward<Args>(args)...))
{
    // 完美转发所有参数
    return std::forward<Func>(func)(std::forward<Args>(args)...);
}

// 使用示例
void foo(int x, double y, std::string z)
{
    std::cout << x << ", " << y << ", " << z << std::endl;
}

invoke(foo, 42, 3.14, std::string("hello"));

// 元组实现
// 变参模板是实现std::tuple的关键：
// 简化的元组实现
template <typename... Types>
class Tuple;

// 空元组特化
template <>
class Tuple<>
{
    // 空元组的实现
};

// 递归情况
template <typename Head, typename... Tail>
class Tuple<Head, Tail...>
{
private:
    Head head;
    Tuple<Tail...> tail;

public:
    Tuple(const Head &h, const Tail &...t)
        : head(h), tail(t...) {}

    Head getHead() const { return head; }
    const Tuple<Tail...> &getTail() const { return tail; }
};

// 可变参数构造函数
// 变参模板可以用于创建接受任意参数的构造函数：
template <typename T>
class Container
{
private:
    std::vector<T> data;

public:
    // 可变参数构造函数
    template <typename... Args>
    Container(Args &&...args) : data{std::forward<Args>(args)...} {}

    // 其他方法...
};

// 使用示例
Container<int> c1(1, 2, 3, 4, 5);            // 使用5个参数初始化
Container<std::string> c2("hello", "world"); // 使用2个参数初始化

// 4.5 参数包的高级操作
// 参数包展开的模式
// 参数包展开可以应用各种模式：
template <typename... Types>
void examples(Types... args)
{
    // 1. 简单展开
    func(args...); // 展开为：func(arg1, arg2, arg3, ...)

    // 2. 对每个参数应用转换
    func(std::forward<Types>(args)...); // 展开为：func(std::forward<Type1>(arg1), std::forward<Type2>(arg2), ...)

    // 3. 构造对象数组
    std::tuple<Types...> tuple(args...);

    // 4. 创建参数包的副本
    anotherFunc(args..., args...); // 展开为：anotherFunc(arg1, arg2, ..., arg1, arg2, ...)
}

// 参数包的索引访问
// 虽然不能直接索引参数包，但可以通过递归或其他技巧实现：
// 使用递归和计数器访问特定索引
template <size_t N, typename Arg, typename... Args>
struct NthElement
{
    using type = typename NthElement<N - 1, Args...>::type;
};

template <typename Arg, typename... Args>
struct NthElement<0, Arg, Args...>
{
    using type = Arg;
};

// 使用示例
using T = NthElement<2, int, double, char, float>::type; // T 是 char

// 4.6 变参模板的限制和注意事项
// 1.不能直接迭代参数包：必须通过递归、折叠表达式或其他技巧处理参数包
// 2.不能直接获取参数包中的特定元素：需要使用递归或其他技巧
// 3.参数包展开只能在特定上下文中使用：
//      函数调用参数
//      初始化列表
//      模板参数列表
//      基类列表
//      表达式列表（如折叠表达式）
// 4.参数包可能为空：设计时需要考虑空参数包的情况

// 4.7 C++17和C++20中的改进
// C++17的改进
// 1.折叠表达式：简化参数包处理
template <typename... Args>
auto sum(Args... args)
{
    return (... + args); // 一元左折叠
}

// 2.类模板参数推导：简化变参模板的使用
std::tuple t(1, "hello", 3.14); // C++17可以省略模板参数

// C++20的改进
// 1.概念约束变参模板：
template <std::integral... Ts>
void integerOnly(Ts... args)
{
    // 只接受整数类型
}

// 2.参数包捕获：
template <typename... Args>
auto makeCapture(Args... args)
{
    return [... args = std::move(args)]()
    {
        // 使用捕获的参数包
    };
}

// 总结
// 变参模板是C++11引入的强大特性，它允许模板接受任意数量的参数。通过参数包和包展开，我们可以创建更灵活、更通用的模板代码。虽然处理参数包有一定的复杂性，但C++17和C++20引入的新特性（如折叠表达式和概念）大大简化了变参模板的使用。

// 变参模板在标准库中得到了广泛应用，如std::tuple、std::make_shared、std::make_unique等。掌握变参模板是进阶C++编程的重要一步。
// 如果类的构造函数需要有输入参数，就是通过变参模板实现的